# 1.导入库
import numpy as np
import pandas as pd
import matplotlib
import seaborn as sns
matplotlib.use('TkAgg')  # 或者根据你的系统选择合适的后端
import matplotlib.pyplot as plt
from matplotlib import rcParams

# rcParams['font.sans-serif'] = 'STHeiti'  # Mac
rcParams['font.sans-serif'] = 'SimHei'  # Win

from contextlib import contextmanager


@contextmanager
def display_all_rows():
    """临时显示所有行的上下文管理器"""
    original_rows = pd.get_option('display.max_rows')
    original_columns = pd.get_option('display.max_columns')
    pd.set_option('display.max_rows', None)
    pd.set_option('display.max_columns', None)
    try:
        yield
    finally:
        pd.set_option('display.max_rows', original_rows)
        pd.set_option('display.max_columns', original_columns)


# 2.导入数据
df = pd.read_csv('../data/pd_data/house_sales.csv')

# 3.数据概览
print('==' * 30)
print('总行数：', len(df))
print('总列数：', len(df.columns))
print('==' * 30)
print(df.info())
print('==' * 30)

# 4.数据清洗
# 删除无用列数据
df.drop(columns='origin_url', inplace=True)
# 缺失值处理
print('==' * 30)
print(df.isna().sum())
df.dropna(inplace=True)
print('==' * 30)

# 检查是否有重复数据
print('重复数据：', df.duplicated().sum())
df.drop_duplicates(inplace=True)

# 数据类型处理
df['area'] = df['area'].str.replace('㎡', '').astype(float)
df['price'] = df['price'].str.replace('万', '').astype(float)
df['toward'] = df['toward'].astype('category')
df['unit'] = df['unit'].str.replace('元/㎡', '').astype(float)
df['year'] = df['year'].str.replace('年建', '').astype(int)
# 异常值处理
# 房屋面积异常值处理
df = df[(df['area'] < 600) & (df['area'] > 20)]
# 房屋售价异常值处理
Q1 = df['price'].quantile(0.25)
Q3 = df['price'].quantile(0.75)
IQR = Q3 - Q1
low_price = Q1 - 1.5 * IQR
high_price = Q3 + 1.5 * IQR
df = df[(df['price'] < high_price) & (df['price'] > low_price)]

# 5.新数据特征构造
# 地区 distinct
df['distinct'] = df['address'].str.split('-').str[0]
# 楼层类型 floor_type
# df['floor_type'] = df['floor'].str.split('（').str[0]
df['floor_type'] = df['floor'].apply(lambda x:
                                     '低层' if '低' in x else
                                     '中层' if '中' in x else
                                     '高层' if '高' in x else '未知').astype('category')
# 是否直辖市 zxs
df['zxs'] = df['city'].apply(lambda x: 1 if x in ['上海', '北京', '天津', '重庆'] else 0)
# print(df[df['zxs'] == 1].sample(10))
# 卧室数量 bed_rooms
# 厅数量 living_rooms
df['bed_rooms'] = df['rooms'].str.split('室').str[0].astype(int)
# df['living_rooms'] = df['rooms'].str.split('室').str[1].str.split('厅').str[0].astype(int)
df['living_rooms'] = df['rooms'].str.extract(r'(\d+)厅').astype(int)
# 楼年龄 building_age
df['building_age'] = 2025 - df['year']
# 价格分段 price_labels
df['price_labels'] = pd.cut(df['price'], bins=4, labels=['超低价', '适中', '略贵', '超高价'])

# 6.问题分析及可视化
print(len(df))
print('==' * 30)
# 使用方式
with display_all_rows():
    print(df.head(5))
print(df.info())


"""
问题编号:A1
问题: 哪些变量最影响房价?面积、楼层、房间数哪个影响更大?
分析主题: 特征相关性
分析目标:了解房屋各特征对房价的线性影响
分组字段:无
指标/方法: 皮尔逊相关系数
"""

print('==' * 30)
coo = df[['price', 'area', 'unit', 'building_age']].corr() # 计算相关性
print(coo)
#                  price      area      unit  building_age
# price         1.000000  0.452523  0.742731      0.091520
# area          0.452523  1.000000 -0.164320     -0.083923
# unit          0.742731 -0.164320  1.000000      0.199128
# building_age  0.091520 -0.083923  0.199128      1.000000
relate = coo['price'].sort_values(ascending=False)[1:]
print(relate)
# unit            0.742731
# area            0.452523
# building_age    0.091520
# 相关性的热力图
# sns.heatmap(coo, cmap='coolwarm')
# plt.title('房屋特性相关性热力图')



"""
问题编号:A2
问题:全国房价总体分布是怎样的?是否存在极端值?
分析主题:描述性统计
分析目标:概览数值型字段的分布特征
分组字段:无
指标/方法:平均数/中位数/四分位数/标准差
"""
print('==' * 30)
with display_all_rows():
    print(df.describe())

# 房价分布直方图
# plt.subplot(111)
# plt.hist(df['price'], bins=10)
# plt.title('房价分布直方图')

# sns.histplot(data=df, x='price', bins=10, kde=True)


"""
问题编号:A6
问题:南北向是否真比单一朝向贵?贵多少?分析主题:朝向溢价
分析目标:评估不同朝向的价格差异
分组字段:toward
指标/方法:方差分析/多重比较
"""
a = df.groupby('toward').agg({
    'price': ['mean', 'median'],
    'unit': 'median',
    'building_age': 'mean',
})
print(a)

# 可视化
plt.figure(figsize=(14, 5))
sns.boxplot(data=df, x='toward', y='price')


plt.grid(True)
plt.tight_layout()
plt.show()
